/************************************************************************
 * @version   --> v1.0
 * @date      --> 2021-04-18
 * @brief     --> 热控模块
 *            1.把当前磁矩板、电源板、飞轮板、热控板上的温度传感器收集的数据
 *              通过串口发送给上位机给上位机，调试时进行串口输出
 *            2.通过串口接收上位机发送的指令，控制磁矩板、电源板、飞轮板、
 *              热控板加热电阻的开关，进行温度控制
 *@update     --> 2021
 ***********************************************************************/

/*Includes ------------------------------------------------------------*/
#include <string.h>                 // 使用内存拷贝
#include <SCoop.h>                  // 使用 SCoop 线程库
#include <MsTimer2.h>            // 使用定时器库
#include <OneWire.h>               // 单线程
#include <DallasTemperature.h>     // DS18B20

/*Private macro ------------------------------------------------------*/
// 宏开关，可启用Serial串口调试
// 正式编译时务必注释掉
//#define SERIAL_DEBUG

/* Private variables --------------------------------------------------*/
// 引脚定义
int SW0 = A7;     // 电源板加热开关引脚
int SW1 = A8;     // 飞轮板加热开关引脚
int SW2 = A9;     // 负载板加热开关引脚
int SW3 = A10;    // 磁矩板加热开关引脚
int SW4 = A11;    // 热控板加热开关引脚
const uint8_t DS18B20_ter = 46;   // 热控板温度，DS18B20数据口引脚
const uint8_t DS18B20_fw = 47;    // 飞轮板温度，DS18B20数据口引脚
const uint8_t DS18B20_pow = 48;   // 电源板温度，DS18B20数据口引脚
const uint8_t DS18B20_mag = 49;   // 磁矩板温度，DS18B20数据口引脚

// 串口数据缓冲区
// 单次收发数据帧限制在22字节以内，第一字节表示热控总开关状态，
// 2-5字节表示热控开关状态，6-22字节表示各模块的设定温度
// 例如：输入——1, 1, 1, 1, 1, 1, 32, 32, 32, 32
// 这里由于还没有设计载荷板，暂时不考虑载荷板温度的设定和测量
uint8_t send_buffer[22];
uint8_t receiver_buffer[22];

// ISR（中断服务程序）中所涉及的变量需要被声明为volatile易变变量
// 状态控制标志量，温度控制
volatile bool thermal_control_on = false;
// 开关状态记录
volatile bool P0_on = false;
volatile bool P1_on = false;
volatile bool P2_on = false;
volatile bool P3_on = false;
volatile bool P4_on = false;

// 温度设定值
volatile float temperature_mag_setting = 38.0;    // 磁矩板设定温度
volatile float temperature_pow_setting = 38.0;    // 电源板设定温度
volatile float temperature_fw_setting = 38.0;     // 飞轮板设定温度
volatile float temperature_ter_setting = 38.0;    // 热控板设定温度

// 温度测量值
volatile float temperature_mag_measurement = 0;   // 磁矩板测量温度
volatile float temperature_pow_measurement = 0;   // 电源板测量温度
volatile float temperature_fw_measurement = 0;    // 飞轮板测量温度
volatile float temperature_ter_measurement = 0;   // 热控板测量温度

// 实例化温度传感器
OneWire oneWire1(DS18B20_mag);
DallasTemperature sensor1(&oneWire1);    // 声明连接在单总线上的单总线设备
OneWire oneWire2(DS18B20_pow);           // 声明一个传感器对象
DallasTemperature sensor2(&oneWire2);
OneWire oneWire3(DS18B20_fw);
DallasTemperature sensor3(&oneWire3);
OneWire oneWire4(DS18B20_ter);
DallasTemperature sensor4(&oneWire4);

// 计时器函数
void pertime()
{
  // 获取传感器摄氏温度数据
  temperature_mag_measurement = sensor1.getTempCByIndex(0);
  temperature_pow_measurement = sensor2.getTempCByIndex(0);
  temperature_fw_measurement = sensor3.getTempCByIndex(0);
  temperature_ter_measurement = sensor4.getTempCByIndex(0);
  // 向总线上的设备发送新一轮的温度转换请求
  sensor1.requestTemperatures();
  sensor2.requestTemperatures();
  sensor3.requestTemperatures();
  sensor4.requestTemperatures();
}

// 通过串口接收上位机的指令
defineTask(ReceiverThread);

void ReceiverThread::setup(){
}

void ReceiverThread::loop(){

  uint8_t i = 0;
  // 串口读取数据
  if(Serial.available()>0){
    delay(20);      // 等待串口数据完全进入缓冲区
  }
  while(Serial.available()>0 && i<22){  // 防止溢出
    receiver_buffer[i] = uint8_t(Serial.read());
    i++;
  }
  // 温度控制开关
  if(receiver_buffer[0] == 1){
    thermal_control_on = true;
  }
  else{
    thermal_control_on = false;
  }
  // 电源板热控开关
  if(receiver_buffer[1] == 1){
    digitalWrite(SW0, HIGH);
    P0_on = true;
    }
  else{
    digitalWrite(SW0, LOW);
    P0_on = false;
    }
   // 飞轮板热控开关
  if(receiver_buffer[2] == 1){
    digitalWrite(SW1, HIGH);
    P1_on = true;
    }
  else{
    digitalWrite(SW1, LOW);
    P1_on = false;
    }
    // 负载板热控开关
  if(receiver_buffer[3] == 1){
    digitalWrite(SW2, HIGH);
    P2_on = true;
    }
  else{
    digitalWrite(SW2, LOW);
    P2_on = false;
    }
    // 磁矩板热控开关
  if(receiver_buffer[4] == 1){
    digitalWrite(SW3, HIGH);
    P3_on = true;
    }
  else{
    digitalWrite(SW3, LOW);
    P3_on = false;
    }
    // 热控板热控开关
  if(receiver_buffer[5] == 1){
    digitalWrite(SW4, HIGH);
    P4_on = true;
    }
  else{
    digitalWrite(SW4, LOW);
    P4_on = false;
    }

  // 模块温度设定
  memcpy(&temperature_mag_setting, &receiver_buffer[6], sizeof(float));
  memcpy(&temperature_pow_setting, &receiver_buffer[10], sizeof(float));
  memcpy(&temperature_fw_setting, &receiver_buffer[14], sizeof(float));
  memcpy(&temperature_ter_setting, &receiver_buffer[18], sizeof(float));

  #ifdef SERIAL_DEBUG
    Serial.println("此时各模块热控工作状态：");
    Serial.print("电源板热控开关：");
    Serial.println(P0_on);
    Serial.print("飞轮板板热控开关：");
    Serial.println(P1_on);
    Serial.print("负载板热控开关：");
    Serial.println(P2_on);
    Serial.print("磁矩板热控开关：");
    Serial.println(P3_on);
    Serial.print("热控板热控开关：");
    Serial.println(P4_on);
    Serial.print("\n");
    Serial.println("各模块设定温度：");
    Serial.print("磁矩板温度设定值：");
    Serial.println(temperature_mag_setting);
    Serial.print("电源板温度设定值：");
    Serial.println(temperature_pow_setting);
    Serial.print("飞轮板温度设定值：");
    Serial.println(temperature_fw_setting);
    Serial.print("热控板温度设定值：");
    Serial.println(temperature_ter_setting);
    Serial.print("\n");
    Serial.println("各模块测量温度：");
    Serial.print("磁矩板测量温度：");
    Serial.print(temperature_mag_measurement);
    Serial.println(" ℃");
    Serial.print("电源板测量温度：");
    Serial.print(temperature_pow_measurement);
    Serial.println(" ℃");
    Serial.print("飞轮板板测量温度：");
    Serial.print(temperature_fw_measurement);
    Serial.println(" ℃");
    Serial.print("温控板测量温度：");
    Serial.print(temperature_ter_measurement);
    Serial.println(" ℃");
  #endif

  sleep(20);  
}

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);   // 使用串口通信
  delay(20);
  mySCoop.start();      // 开启线程调度
  // 热控开关
  pinMode(SW0, OUTPUT);
  pinMode(SW1, OUTPUT);
  pinMode(SW2, OUTPUT);
  pinMode(SW3, OUTPUT);
  pinMode(SW4, OUTPUT);
  // 温度传感器初始化
  sensor1.begin();
  sensor1.setWaitForConversion(false);    // 设置为非阻塞模式
  sensor1.requestTemperatures();
  sensor2.begin();
  sensor2.setWaitForConversion(false);    // 设置为非阻塞模式
  sensor2.requestTemperatures();
  sensor3.begin();
  sensor3.setWaitForConversion(false);    // 设置为非阻塞模式
  sensor3.requestTemperatures();
  sensor4.begin();
  sensor4.setWaitForConversion(false);    // 设置为非阻塞模式
  sensor4.requestTemperatures();
  
  // 定时器中断
  MsTimer2::set(500, pertime);    // 定时器定时500ms，更新一次传感器数据
  MsTimer2::start();
}

void loop() {
  // put your main code here, to run repeatedly:
  // 直接使用开关量进行温度控制
  if(thermal_control_on){
    if(temperature_pow_measurement < temperature_pow_setting-2){
      digitalWrite(SW0, HIGH);
    }
    else if(temperature_pow_measurement > temperature_pow_setting+2){
      digitalWrite(SW0, LOW);
    }
    if(temperature_fw_measurement < temperature_fw_setting-2){
      digitalWrite(SW1, HIGH);
    }
    else if(temperature_fw_measurement > temperature_fw_setting+2){
      digitalWrite(SW1, LOW);
    }
    if(temperature_mag_measurement < temperature_mag_setting-2){
      digitalWrite(SW3, HIGH);
    }
    else if(temperature_mag_measurement > temperature_mag_setting+2){
      digitalWrite(SW3, LOW);
    }
    if(temperature_ter_measurement < temperature_ter_setting-2){
      digitalWrite(SW4, HIGH);
    }
    else if(temperature_ter_measurement > temperature_ter_setting+2){
      digitalWrite(SW4, LOW);
    }
  }
  else{
    digitalWrite(SW0, LOW);
    digitalWrite(SW1, LOW);
    digitalWrite(SW2, LOW);
    digitalWrite(SW3, LOW);
    digitalWrite(SW4, LOW);
  }

  // 通过串口发送温度数据到上位机
  send_buffer[0] = thermal_control_on;
  send_buffer[1] = P0_on;
  send_buffer[2] = P1_on;
  send_buffer[3] = P2_on;
  send_buffer[4] = P3_on;
  send_buffer[5] = P4_on;
  memcpy(&send_buffer[6], &temperature_pow_measurement, sizeof(float));
  memcpy(&send_buffer[10], &temperature_fw_measurement, sizeof(float));
  memcpy(&send_buffer[14], &temperature_mag_measurement, sizeof(float));
  memcpy(&send_buffer[18], &temperature_ter_measurement, sizeof(float));
  Serial.write(send_buffer, 22);
  sleep(25);
  
  yield();          // 让给另一个线程 
}
